package client

import (
	"fmt"
	"reflect"

	"starsine.cn/appTpl/cmn/config/aconf"
	. "starsine.cn/appTpl/cmn/defs"
	"starsine.cn/appTpl/cmn/logs"
	"starsine.cn/appTpl/svccenter/registry/grpc/scregistryproto"
	"starsine.cn/appTpl/svclog/peers/client/svcdemo"
	"starsine.cn/appTpl/svcpeers"
	"starsine.cn/appTpl/svcpeers/svcpeersproto"
	registryclient "starsine.cn/appTpl/svcregistry/grpc/client"
)

const (
	SVC_DEPEND_DEMO = iota

	SVC_DEPEND_NUM
)

var svcDepName [SVC_DEPEND_NUM]string = [SVC_DEPEND_NUM]string{
	"svcdemo",
}

func GetSvcDepType(in string) int {
	for i := SVC_DEPEND_DEMO; i < SVC_DEPEND_NUM; i++ {
		if svcDepName[i] == in {
			return i
		}
	}
	return -1
}

type SvcAddrInfo struct {
	instanceID string
	addr       string
}

type SvcAddr struct {
	index int
	addr  []SvcAddrInfo
}

type SvcDepRpcInfo struct {
	svcType  string
	version  int32
	required bool
	addr     SvcAddr
}

func (t SvcDepRpcInfo) String() (str string) {
	return fmt.Sprintf("svcType: %s, version: %d, required:%t, addr: %v",
		t.svcType, t.version, t.required, t.addr)
}

type SvcDeps struct {
	userSpace string
	data      [SVC_DEPEND_NUM]SvcDepRpcInfo

	svcdemo *svcdemo.GRpcClient

	appCtxt *AppContext
}

func (t SvcDeps) String() (str string) {
	str += fmt.Sprintln("SvcDeps:")
	str += fmt.Sprintln("userSpace: ", t.userSpace)
	for _, v := range t.data {
		str += fmt.Sprintln(v)
	}

	return str
}

func CreateSvcDeps(appCtxt *AppContext) *SvcDeps {
	p := new(SvcDeps)
	p.userSpace = aconf.GetString("User", "default", aconf.WithENV("svc_user"))
	p.data = [SVC_DEPEND_NUM]SvcDepRpcInfo{}

	p.appCtxt = appCtxt

	p.appPeerInit()
	return p
}

func (p *SvcDeps) appPeerInit() {
	p.SetSvc(SVC_DEPEND_DEMO, SvcDepRpcInfo{
		svcType:  svcDepName[SVC_DEPEND_DEMO],
		version:  0,
		required: true,
		addr: SvcAddr{
			index: 0,
			addr:  nil,
		},
	})
	p.svcdemo = nil
}

func (p *SvcDeps) SetSvc(svcDepType int, svc SvcDepRpcInfo) {
	p.data[svcDepType] = svc
}

func (p *SvcDeps) GetSvc(svcDepType int) SvcDepRpcInfo {
	return p.data[svcDepType]
}

func (p *SvcDeps) GetHandler(svcDepType int) interface{} {
	switch svcDepType {
	case SVC_DEPEND_DEMO:
		return p.svcdemo
	default:
		return nil
	}
}

func (p *SvcDeps) ResetHandler(svcDepType int) {
	switch svcDepType {
	case SVC_DEPEND_DEMO:
		if p.svcdemo != nil {
			p.svcdemo.End()
			p.svcdemo = nil
		}
	default:
	}
}

func (p *SvcDeps) CreateHandler(addr string, svcDepType int) interface{} {
	switch svcDepType {
	case SVC_DEPEND_DEMO:
		client := svcdemo.CreateGRpcClient(p.appCtxt, addr)
		err := client.Start()
		if err != nil {
			logs.Errorf("client.Start(grpc peer:%s) fail, err: %s", addr, err)
			return nil
		}
		p.svcdemo = client
		return client
	default:
		return nil
	}
}

func (p *SvcDeps) SvcDepGet(svcDepType int) error {
	if svcDepType >= SVC_DEPEND_NUM {
		return fmt.Errorf("SvcDepGet error input with svcDepType=%d", svcDepType)
	}

	svc, err := registryclient.SvcGet(&scregistryproto.SvcGetReq{
		UserSpace: p.userSpace,
		SvcType:   p.data[svcDepType].svcType,
	})
	if err != nil {
		return fmt.Errorf("SvcGet(Userspace=%s, SvcType=%s) fail, err: %s", p.userSpace, p.data[svcDepType].svcType, err)
	} else {
		addr := SvcAddr{}
		for _, v := range svc.SvcAddr {
			addr.addr = append(addr.addr, SvcAddrInfo{
				addr:       v.RpcAddr,
				instanceID: v.InstanceID,
			})
		}
		addr.index = 0
		if len(addr.addr) == 0 {
			return fmt.Errorf("svcdemo(svcType:%s) svc get 0", p.data[svcDepType].svcType)
		}
		p.data[svcDepType].addr = addr
		p.CreateHandler(p.data[svcDepType].addr.addr[0].addr, svcDepType)
		logs.Debugf("svcdemo(svcType:%s, instanceID:%s, addr:%s) start success", p.data[svcDepType].svcType, svc.SvcAddr[0].InstanceID, svc.SvcAddr[0].RpcAddr)
		return nil
	}
}

func (p *SvcDeps) SvcDepInitGet() {
	for i := SVC_DEPEND_DEMO; i < SVC_DEPEND_NUM; i++ {
		if reflect.ValueOf(p.GetHandler(i)).IsNil() {
			err := p.SvcDepGet(i)
			if err != nil {
				logs.Warnf("SvcDepInitGet.SvcDepGet(%s) fail, err: %s", svcDepName[i], err)
				continue
			}
			logs.Debugf("SvcDepInitGet.SvcDepGet(%s) ok", svcDepName[i])
		}
	}
}

func (p *SvcDeps) Shakehand(svcDepType int) bool {

	switch svcDepType {
	case SVC_DEPEND_DEMO:
		if p.svcdemo == nil {
			return false
		}

	default:
		return false
	}

	r, err := svcpeers.GetSvcClient(p.data[svcDepType].addr.addr[0].instanceID, p.data[svcDepType].addr.addr[0].addr).SvcShakehand(&svcpeersproto.ShakehandReq{
		Version: p.data[svcDepType].version,
	})
	if err != nil {
		logs.Warnf("Shakehand(%s) [addr: %s, instance id: %s] fail, err: %s", svcDepName[svcDepType], p.data[svcDepType].addr.addr[0].addr, p.data[svcDepType].addr.addr[0].instanceID, err)
		return false
	}
	return r.Succ
}

func (p *SvcDeps) HealthCheck(svcDepType int) bool {
	if svcDepType >= SVC_DEPEND_NUM {
		return false
	}

	return p.Shakehand(svcDepType)
}
